package gateway

import (
	"encoding/json"
	"github.com/Unixeno/TheMoon/planet/orm/model"
	"github.com/Unixeno/TheMoon/planet/orm/zone"
	"github.com/Unixeno/TheMoon/planet/zoner"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	"net/http"
	"strconv"
)

// swagger:parameters CreateZone
type AddZoneParam struct {
	// 需要新建Zone的名字
	//
	// Required: true
	// in: query
	Name string `json:"name"`
}

// Zone ID
// swagger:response ZoneIDResponse
type ZoneIDResponse struct {
	// in: body
	Body ZoneID
}

// Success
//
// swagger:response EmptyResponse
type empty struct{}

// Zone List
//
// swagger:response ZoneListResponse
type ZoneListResponse []zone.ZoneList

// Zone信息
// swagger:model ZoneInfo
type ZoneInfo model.Zone

// Zone 详细信息
// swagger:model ZoneDetails
type ZoneDetails struct {
	model.Zone
}

// swagger:parameters RemoveZone InspectZone
type ZoneID struct {
	ID string
}

// swagger:route POST /zone/create Zone CreateZone
//
// CreateZone
//
// 创建一个Zone
//
//     Responses:
//       200: ZoneIDResponse
//       409: ConflictError
//       500: ErrorResponse
func CreateZone(w http.ResponseWriter, r *http.Request) {
	param := AddZoneParam{}
	err := json.NewDecoder(r.Body).Decode(&param)
	if err != nil {
		logger.WithError(err).Error("Invalid requests parameter")
		WriteResponseCode(w, 500, InvalidParam)
		return
	}
	dao := zone.New()
	dao.Name = param.Name
	id, err := dao.Create()
	if err == nil {
		WriteResponse(w, ZoneID{ID: id})
		return
	} else if err == zone.ExistError {
		WriteResponseCode(w, 409, ErrorResponse{Message: "Zone Already Exist"})
		return
	} else {
		logger.WithError(err).Error("create zone failed")
		WriteResponseCode(w, 500, ServerError)
		return
	}
}

// swagger:route GET /zone Zone ListZone
//
// ListZone
//
// 返回所有Zone
//
//     Responses:
//       200: ZoneListResponse
//       500: ErrorResponse
func ListZone(w http.ResponseWriter, r *http.Request) {
	dao := zone.New()
	list, err := dao.List()
	if err != nil {
		WriteResponseCode(w, 500, ServerError)
		return
	}
	WriteResponse(w, list)
}

// swagger:route DELETE /zone/{id} Zone RemoveZone
//
// RemoveZone
//
// 删除一个Zone
//
//     Responses:
//       204: EmptyResponse
//       500: ErrorResponse
func RemoveZone(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	logger.WithField("id", id).Debug("request to remove zone")
	dao := zone.New()
	dao.ID = id
	if err := dao.Delete(); err == nil {
		WriteResponseCode(w, 204, nil)
		return
	} else if gorm.IsRecordNotFoundError(err) {
		WriteResponseCode(w, 404, ErrorResponse{Message: "Zone not found"})
		return
	} else {
		WriteResponseCode(w, 500, ServerError)
		return
	}
}

// swagger:route GET /zone/{id} Zone InspectZone
//
// InspectZone
//
// 查询一个Zone的详细信息
//
//     Responses:
//       200: body:ZoneDetails
//		 404: NotFoundError
//       500: ErrorResponse
func InspectZone(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	dao := zone.New()
	dao.ID = id
	if details, err := dao.GetDetails(); err == nil {
		WriteResponse(w, details)
		return
	} else if gorm.IsRecordNotFoundError(err) {
		WriteResponseCode(w, 404, ErrorResponse{Message: "Zone not found"})
		return
	} else {
		WriteResponseCode(w, 500, ServerError)
		return
	}
}

// swagger:parameters AddMoonToZone RemoveMoonFromZone
type MoonIDs struct {
	// list of moon id
	// in:body
	Body []string
	// zone id
	// in: path
	ID string
}

// swagger:route POST /zone/{id}/add Zone AddMoonToZone
//
// AddMoonToZone
//
// 将moon节点添加进入一个zone
//
//     Responses:
//       204: EmptyResponse
//		 404: NotFoundError
//       500: ErrorResponse
func AddMoonToZone(w http.ResponseWriter, r *http.Request) {
	var moonList []string
	zoneID := mux.Vars(r)["id"]
	if err := json.NewDecoder(r.Body).Decode(&moonList); err != nil || zoneID == "" {
		WriteResponseCode(w, 500, InvalidParam)
		return
	}
	logger.WithField("zone", zoneID).Debug("add moon to zone")
	dao := zone.New()
	dao.ID = zoneID
	err := dao.AddMoon(moonList)
	if err == nil {
		WriteResponse(w, nil)
		return
	} else if err == gorm.ErrRecordNotFound {
		WriteResponseCode(w, 404, ErrorResponse{Message: "Zone dose not exist"})
		return
	} else {
		logger.WithError(err).Error("failed to add moons to zone")
		WriteResponseCode(w, 500, ErrorResponse{Message: err.Error()})
		return
	}
}

// swagger:route POST /zone/{id}/remove Zone RemoveMoonFromZone
//
// RemoveMoonFromZone
//
// 从zone中删除moon
//
//     Responses:
//       204: EmptyResponse
//		 404: NotFoundError
//       500: ErrorResponse
func RemoveMoonFromZone(w http.ResponseWriter, r *http.Request) {
	var moonList []string
	zoneID := mux.Vars(r)["id"]
	if err := json.NewDecoder(r.Body).Decode(&moonList); err != nil || zoneID == "" {
		WriteResponseCode(w, 500, InvalidParam)
		return
	}
	logger.WithField("zone", zoneID).Debug("remove moon from zone")
	dao := zone.New()
	dao.ID = zoneID
	err := dao.RemoveMoon(moonList)
	if err == nil {
		for _, moonID := range moonList {
			zoner.Leave(zoneID, moonID)
		}
		WriteResponse(w, nil)
		return
	} else if err == gorm.ErrRecordNotFound {
		WriteResponseCode(w, 404, ErrorResponse{Message: "Zone dose not exist"})
		return
	} else {
		logger.WithError(err).Error("failed to remove moons to zone")
		WriteResponseCode(w, 500, ErrorResponse{Message: err.Error()})
		return
	}
}

// swagger:parameters AddZonefile
type AddZonefileParam struct {
	// zone id
	// Required: true
	// in:path
	ID string `json:"id"`

	// zonefile规则
	// Required: true
	// in: query
	Zonefile string `json:"zonefile"`
}

// Success
// swagger:response AddZonefileResp
type AddZonefileResp struct {
	// in:body
	Body struct {
		// 新加入的zonefile的版本号
		Version int
	}
}

// swagger:route POST /zone/{id}/zonefile/add Zone AddZonefile
//
// AddZonefile
//
// 像zone内添加一个zonefile
//
//     Responses:
//       200: AddZonefileResp
//		 404: NotFoundError
//       500: ErrorResponse
func AddZonefile(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	logger.WithField("zone", id).Debug("add zone file")
	zonefile := AddZonefileParam{}
	if err := json.NewDecoder(r.Body).Decode(&zonefile); err != nil || id == "" || zonefile.Zonefile == "" {
		WriteResponseCode(w, 500, InvalidParam)
		return
	}
	dao := zone.New()
	dao.ID = id
	if gorm.IsRecordNotFoundError(dao.Get()) {
		WriteResponseCode(w, 404, ErrorResponse{Message: "Zone dose not exist"})
		return
	}
	if version, err := dao.AddZonefile(zonefile.Zonefile); err != nil {
		logger.WithError(err).WithField("id", id).Error("failed to add zone file to zone")
		WriteResponseCode(w, 500, ServerError)
		return
	} else {
		WriteResponse(w, struct{ Version int }{Version: version})
	}
}

// Zonefile List
// swagger:response ListZonefileResp
type ListZonefileResp []model.Zonefile

// ListZonefileParam
// swagger: parameters ListZoneFile
type ListZoneFileParam struct {
	// zone id
	// required: true
	// in:path
	ID int
	// 条数限制, 0则不限制
	// in:query
	Limit int
	// 查询起始版本
	// in:query
	Begin int
	// 是否使用倒序
	// in: query
	Desc bool
}

// swagger:route Get /zone/{id}/zonefile Zone ListZoneFile
//
// ListZonefile
//
// 列出此zone下的所有zonefile
//
//     Responses:
//       200: ListZonefileResp
//		 404: NotFoundError
//       500: ErrorResponse
func ListZonefile(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	logger.WithField("zone", id).Debug("list zonefile")
	param := ListZoneFileParam{}

	urlParam := r.URL.Query()
	param.Limit, _ = strconv.Atoi(urlParam.Get("limit"))
	param.Begin, _ = strconv.Atoi(urlParam.Get("begin"))
	param.Desc, _ = strconv.ParseBool(urlParam.Get("desc"))
	dao := zone.New()
	dao.ID = id
	zonefiles, err := dao.ListZonefile(int32(param.Begin), int32(param.Limit), param.Desc)
	if err != nil {
		if gorm.IsRecordNotFoundError(dao.Get()) {
			WriteResponseCode(w, 404, ErrorResponse{Message: "Zonefile dose not exist"})
			return
		}
		WriteResponseCode(w, 500, ServerError)
	}
	WriteResponse(w, zonefiles)
}

// swagger:parameters DeployZonefile GetZonefile
type ZonefileParam struct {
	// zone id
	// required: true
	// in:path
	ID string `json:"id"`

	// zonefile版本
	// required: true
	// in: path
	Version int `json:"version"`
}

// Zonefile
// swagger:response ZonefileResp
type ZonefileResp struct {
	// in:body
	Body model.Zonefile
}

// swagger:route Get /zone/{id}/zonefile/{version} Zone GetZonefile
//
// GetZonefile
//
// 获取zone下指定版本的zonefile
//
//     Responses:
//       200: ZonefileResp
//		 404: NotFoundError
//       500: ErrorResponse
func GetZonefile(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]
	logger.WithField("zone", id).Debug("get zonefile")
	version, _ := strconv.Atoi(mux.Vars(r)["version"])
	dao := zone.New()
	dao.ID = id
	zonefile, err := dao.GetZonefile(version)
	if err != nil {
		if gorm.IsRecordNotFoundError(err) {
			WriteResponseCode(w, 404, ErrorResponse{Message: "Zonefile dose not exist"})
			return
		}
		WriteResponseCode(w, 500, ServerError)
	}
	WriteResponse(w, zonefile)
}

// swagger:route POST /zone/{id}/zonefile/{version}/deploy Zone DeployZonefile
//
// DeployZonefile
//
// 部署一个zonefile
//
//     Responses:
//       204: EmptyResponse
//		 404: NotFoundError
//       500: ErrorResponse
func DeployZonefile(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]
	version, _ := strconv.Atoi(vars["version"])
	logger.WithField("id", id).WithField("version", version).Debug("request to deploy")
	dao := zone.New()
	dao.ID = id
	if gorm.IsRecordNotFoundError(dao.Get()) {
		WriteResponseCode(w, 404, ErrorResponse{Message: "Zone dose not exist"})
		return
	}
	err := dao.Deploy(version)
	if err != nil {
		if gorm.IsRecordNotFoundError(err) {
			WriteResponseCode(w, 404, ErrorResponse{Message: "Zonefile dose not exist"})
			return
		}
		WriteResponseCode(w, 500, ServerError)
		return
	}
	if err = zoner.Deploy(id); err != nil {
		logger.WithError(err).Error()
		WriteResponseCode(w, 500, ServerError)
		return
	}
	WriteResponseCode(w, 204, nil)
}
